#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include "drv_common_def.h"
#include "drv_serial_port.h"
#include "drv_sys.h"
#include "shell.h"
typedef struct ShellCmd_t ShellCmd;
struct ShellCmd_t {
    ShellCmd *next;
    const char *name;
    ShellCmdFunc func;
};
static ShellCmd *g_shellCmdList = NULL;
void RegisterShellCmd(ShellCmdEntry *cmd)
{
    assert(cmd != NULL);
    ShellCmd *shellCmd = (ShellCmd *)malloc(sizeof(ShellCmd));
    if (shellCmd == NULL) {
        return;
    }
    shellCmd->func = cmd->func;
    shellCmd->name = cmd->name;
    shellCmd->next = g_shellCmdList;
    g_shellCmdList = shellCmd;
}
void ShellCmd_SayHello(const ShellCmdArgs *args)
{
    printf("cmd name is hello\n");
    uint8_t cnt = 0;
    const char *arg;
    while (cnt < args->argsNum) {
        arg = args->getArg(cnt);
        printf("#%u arg is %s\n", cnt, arg);
        cnt++;
    }
}
EXPORT_SHELL_CMD("hello", ShellCmd_SayHello);

void ShellCmd_Reboot(const ShellCmdArgs *args)
{
    System_Reset();
}
EXPORT_SHELL_CMD("reboot", ShellCmd_Reboot);

extern int __start_shell_cmd;
extern int __stop_shell_cmd;
#define SHELL_CMD_START (ShellCmdEntry *)&__start_shell_cmd
#define SHELL_CMD_END (ShellCmdEntry *)&__stop_shell_cmd
ABS_STATIC_INLINE void PrintPrompt(void)
{
    printf("# ");
    fflush(NULL);
}
void ProbeShellCmdList(void)
{
    ShellCmdEntry *item = SHELL_CMD_START;
    for (item = SHELL_CMD_START; item != SHELL_CMD_END; item++) {
        RegisterShellCmd(item);
    }
}
uint8_t g_argsPos[20] = {0};
char *g_shellArgsBuffer = NULL;
ShellCmdArgs *g_shellCmdArgs = NULL;
const char *_ShellArgs_GetArg(uint8_t argIdx)
{
    if (argIdx >= g_shellCmdArgs->argsNum || g_shellCmdArgs == NULL) {
        return NULL;
    }
    return g_shellArgsBuffer + g_argsPos[argIdx + 1];
}
const char *_ShellArgs_GetCmdName(void)
{
    if (g_shellCmdArgs == NULL) {
        return NULL;
    }
    return g_shellArgsBuffer + g_argsPos[0];
}
ShellCmdArgs *ShellArgsCtor(void)
{
    ShellCmdArgs *me = (ShellCmdArgs *)malloc(sizeof(ShellCmdArgs));
    me->argsNum = 0;
    me->getArg = _ShellArgs_GetArg;
    me->getName = _ShellArgs_GetCmdName;
    return me;
}
void Shell_Init(void)
{
    ProbeShellCmdList();
    g_shellCmdArgs = ShellArgsCtor();
    PrintPrompt();
}
const ShellCmd *FindShellCmd(const char *name)
{
    if (name == NULL || g_shellCmdList == NULL) {
        return NULL;
    }
    ShellCmd *shellCmd = g_shellCmdList;
    while (shellCmd != NULL) {
        if (strcmp(name, shellCmd->name) == 0) {
            return shellCmd;
        }
        shellCmd = shellCmd->next;
    }
    printf("cmd %s is not found\n", name);
    return NULL;
}
typedef struct {
    uint16_t start;
    uint16_t end;
} ArgRange;
void GetNextArgRange(const char *buffer, ArgRange *arg)
{
    uint16_t pos = 0;
    uint16_t totalLen = strlen(buffer);
    while (isspace(buffer[pos]) && pos < totalLen) {
        pos++;
    }
    arg->start = pos;
    while (!isspace(buffer[pos]) && pos < totalLen) {
        pos++;
    }
    arg->end = pos;
}
void ParseArgs(char *args)
{
    uint16_t offset = 0;
    ArgRange range = {0};
    g_shellCmdArgs->argsNum = 0;
    uint16_t totalLen = strlen(args);
    while (offset < totalLen && g_shellCmdArgs->argsNum < 20) {
        GetNextArgRange(args + offset, &range);
        args[offset + range.end] = 0;
        g_argsPos[g_shellCmdArgs->argsNum++] = offset + range.start;
        offset += range.end + 1;
    }
}

void DebugUartCallBack(const uint8_t *buf, size_t len)
{
    char buffer[100];
    memcpy(buffer, buf, 100);
    g_shellArgsBuffer = buffer;
    printf("%s\n", g_shellArgsBuffer);
    ParseArgs(g_shellArgsBuffer);
    const char *name = g_shellCmdArgs->getName();
    g_shellCmdArgs->argsNum--;
    const ShellCmd *shellCmd = FindShellCmd(name);
    if (shellCmd != NULL) {
        shellCmd->func(g_shellCmdArgs);
    }
    PrintPrompt();
}